package com.lanyou.cook.util;

import java.util.Properties;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.Key;
import java.security.KeyPair;
import java.security.KeyFactory;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.Security;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map; 
//import org.apache.commons.codec.binary.Base64;
import java.util.Base64;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;

import org.apache.commons.lang.StringUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.encoders.Hex;

import com.lanyou.cook.common.Constants;
//import com.lanyou.cook.web.BigInteger;
import java.math.BigInteger;

@SuppressWarnings("unused")
public class RSAUtil {
    public static final String KEY_ALGORITHM = "RSA";
    public static final String SIGNATURE_ALGORITHM = "MD5withRSA";
    private static final String PUBLIC_KEY = "RSAPublicKey";
    private static final String PRIVATE_KEY = "RSAPrivateKey";
 
	public static String getPublicKey(Map<String, Object> keyMap) throws Exception {
         Key key = (Key) keyMap.get(PUBLIC_KEY); 
         byte[] publicKey = key.getEncoded(); 
 		 String test = bytes2HexString(publicKey);
         return Base64.getEncoder().encodeToString(key.getEncoded());
	}
	public static String getPrivateKey(Map<String, Object> keyMap) throws Exception {
         Key key = (Key) keyMap.get(PRIVATE_KEY); 
         byte[] privateKey =key.getEncoded(); 
         return Base64.getEncoder().encodeToString(key.getEncoded());
	}  
	 
	public static Map<String, Object> initKey() throws Exception {
		Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());  
		KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM, new BouncyCastleProvider());
		keyPairGen.initialize(1024);
		KeyPair keyPair = keyPairGen.generateKeyPair();
		RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
		RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
		String exponent = publicKey.getPublicExponent().toString(16);
		String modulus = publicKey.getModulus().toString(16);
		
		Map<String, Object> keyMap = new HashMap<String, Object>(2);
		keyMap.put(PUBLIC_KEY, publicKey);
		keyMap.put(PRIVATE_KEY, privateKey);
		String pub = getPublicKey(keyMap);
		String pri = getPrivateKey(keyMap);
		return keyMap;
	}
	public static String decryptByPrivateKey(String encryptedStr, String privateKey)
            throws Exception {
		//initKey();
		//BigInteger将16进制字符串转成byte[]出现问题，比如将大于等于'80'开头的16进制字符串，结果会多一个字节(byte[0]=0)
		byte[] encryptedData = Hex.decode(encryptedStr);//new BigInteger(encryptedStr, 16).toByteArray();
		
        byte[] keyBytes = Base64.getDecoder().decode(privateKey);
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);
        Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());  
        Cipher cipher = Cipher.getInstance("RSA", new BouncyCastleProvider());
        cipher.init(Cipher.DECRYPT_MODE, privateK);
        int inputLen = encryptedData.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offset = 0;
        byte[] cache = null;
        int i = 0;
        int blockSize = 128;
     // 对数据分段解密
        while (inputLen - offset > 0) {
            if (inputLen - offset > blockSize) {
            	try {
                cache = cipher.doFinal(encryptedData, offset, blockSize);
            	} catch (IllegalBlockSizeException e) {
            		e.printStackTrace();
            	} catch (BadPaddingException e) {
            		e.printStackTrace();
            	}
            } else {
                cache = cipher.doFinal(encryptedData, offset, inputLen - offset);
            }
            out.write(cache, 0, cache.length);
            i++;
            offset = i * blockSize;
        }
        byte[] decryptedData = out.toByteArray();
        out.close();
        String decryptStr = new String(decryptedData);
        return StringUtils.reverse(decryptStr);
        /*byte[] data = ASCII2BCD(encryptedData, encryptedData.length);
        int inputLen = data.length;
        String decryptStr = "";
        byte[][] arrays = splitArray(data, 64);
        for(byte[] arr:arrays) {
        	 try {
             	decryptStr += new String(cipher.doFinal(arr));
             } catch (BadPaddingException e) {
             	e.printStackTrace();
             }
        }*/
        //return StringUtils.reverse(decryptStr);
    }
	public static String bytes2HexString(byte[] srcByteData) {
		StringBuilder stringBuilder = new StringBuilder("");
		if(srcByteData == null || srcByteData.length <= 0)
		{
			return null;
		}
		for(int i = 0; i < srcByteData.length; ++i){
			int v = srcByteData[i] & 0xFF;
			String hv = Integer.toHexString(v);
			if(hv.length() < 2) {
				stringBuilder.append(0);
			}
			stringBuilder.append(hv);
		}
		return stringBuilder.toString();
	}
	public static byte[] ASCII2BCD(byte[] ascii, int asc_len) {
		int bcd_len = asc_len % 2 == 0? asc_len / 2: (asc_len+1) / 2;
		byte[] bcd = new byte[bcd_len];
		int j = 0;
		for(int i = 0; i < (asc_len + 1)/2; i++) {
			bcd[i] = asc2bcd(ascii[j++]);
			bcd[i] = (byte)(((j >= asc_len)? 0x00:asc2bcd(ascii[j++])) + (bcd[i] << 4));
		}
		return bcd;
	}
	public static byte asc2bcd(byte asc) {
		byte bcd;
		if(asc >= '0' && asc <= '9') {
			bcd = (byte)(asc-'0');
		}
		else if (asc >= 'A' && asc <= 'F') {
			bcd = (byte)(asc-'A'+10);
		}
		else if(asc >= 'a' && asc <= 'f') {
			bcd = (byte)(asc-'a'+10);
		}
		else {
			bcd = (byte)(asc-48);
		}
		return bcd;
	}
	/**  
     *拆分数组   
     */    
    public static byte[][] splitArray(byte[] data,int len){    
        int x = data.length / len;    
        int y = data.length % len;    
        int z = 0;    
        if(y!=0){    
            z = 1;    
        }    
        byte[][] arrays = new byte[x+z][];    
        byte[] arr;    
        for(int i=0; i<x+z; i++){    
            arr = new byte[len];    
            if(i==x+z-1 && y!=0){    
                System.arraycopy(data, i*len, arr, 0, y);    
            }else{    
                System.arraycopy(data, i*len, arr, 0, len);    
            }    
            arrays[i] = arr;    
        }    
        return arrays;    
    } 
}